using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace EForms
{
	public enum MenuStyle
	{
		Flat = 0,
		Default = 1
	}
	public class EFormEx : Form
	{
		private System.ComponentModel.Container components = null;

		[DllImport("User32.dll",CharSet = CharSet.Auto)]
		public static extern IntPtr SetWindowLong(IntPtr hwnd, int index, MyWndProc my);

		[DllImport("User32.dll",CharSet = CharSet.Auto)]
		public static extern IntPtr SetWindowsHookEx(int type, HookProc hook, 
			IntPtr instance, int threadID);

		//Function pointer to new window proc
		public delegate int MyWndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
		//Function pointer to hook proc
		public delegate int HookProc(int code, IntPtr wparam, ref WinAPI.CWPSTRUCT cwp);

		IntPtr defaultWndProc = IntPtr.Zero;//Pointer to menu's default WndProc
		MyWndProc subWndProc;//Delegate of type MyWndProc - needed to subclass the window
		HookProc hookProc;//This is delegate of type HookProc - needed to process the hooked window
		IntPtr hookHandle = IntPtr.Zero;//Pointer to hookProc
		Color borderColor;
		MenuStyle menuStyle;
		
		public EFormEx()
		{
			InitializeComponent();

			//Construct the delegate that will process the hooked windows proc
			hookProc = new HookProc(Hooked);
			//Construct the delegate that will process the subclassed windows proc
			subWndProc = new MyWndProc(SubclassWndProc);
			//Set the default border color
			borderColor = Color.Black;
			//Default menu style
			menuStyle = MenuStyle.Flat;

			this.BackColor = EForms.Themes.Colors.ApplicationBackground;

			//Check for menu style
			if(menuStyle == MenuStyle.Flat)
				hookHandle = SetWindowsHookEx(4, hookProc, IntPtr.Zero ,
					WinAPI.GetWindowThreadProcessId(Handle,0));
		}

		#region Subclassed wndProc

		int Hooked(int code, IntPtr wparam, ref WinAPI.CWPSTRUCT cwp)
		{
			switch(code)
			{
				case 0://HC_ACTION -> this means that the hook procedure should process the message
					//contained in CWPSTRUCT
				switch(cwp.message)
				{
					case 0x0001://WM_CREATE - catch this before the window is created
						string s = string.Empty;
						char[] className = new char[10];
						//Get the window class name
						int length = WinAPI.GetClassName(cwp.hwnd,className,9);
						//Convert it to string
						for(int i=0;i<length;i++)
							s += className[i];
						//Now check if the window is a menu
						if(s == "#32768")//System class for menu
							//if true - subclass the window
							defaultWndProc = SetWindowLong(cwp.hwnd, (-4), subWndProc);
						break;
				}
					break;
			}
			return WinAPI.CallNextHookEx(hookHandle,code,wparam, ref cwp);
		}

		int SubclassWndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam)
		{
			//tracer.Items.Add(msg.ToString());
			switch(msg)
			{
				case 0x0085://WM_NCPAINT
					IntPtr menuDC  = WinAPI.GetWindowDC(hwnd);					
					Graphics g = Graphics.FromHdc(menuDC);

					DrawBorder(g);
                    
					WinAPI.ReleaseDC(hwnd,menuDC);
					g.Dispose();
					return 0;					
				case 0x0317://WM_PRINT
					int result = WinAPI.CallWindowProc(defaultWndProc,hwnd,msg,wparam,lparam);
					menuDC  = wparam;
					g = Graphics.FromHdc(menuDC);
					//Draw the border around the menu
					DrawBorder(g);
                    
					WinAPI.ReleaseDC(hwnd,menuDC);
					g.Dispose();
					return result;
			}			
			return WinAPI.CallWindowProc(defaultWndProc,hwnd,msg,wparam,lparam);
		}

		#endregion

		#region Support methods

		void DrawBorder(Graphics g)
		{
			Rectangle r = new Rectangle(0,0,(int)g.VisibleClipBounds.Width-1, 
				(int)g.VisibleClipBounds.Height-1);					
			Rectangle r1 = new Rectangle(1,1,(int)g.VisibleClipBounds.Width-3, 
				(int)g.VisibleClipBounds.Height-3);
			Rectangle r2 = new Rectangle(2,2,(int)g.VisibleClipBounds.Width-5, 
				(int)g.VisibleClipBounds.Height-5);

			g.DrawRectangle(new Pen(borderColor),r);
			g.DrawRectangle(new Pen(SystemColors.Menu),r1);
			g.DrawRectangle(new Pen(SystemColors.Menu),r2);
		}

		#endregion

		#region Overriden methods

		#region MainForm WndProc

		protected override void WndProc(ref Message m)
		{
			switch(m.Msg)
			{
				case 0x0002://WM_DESTROY -> we have to unhook the hooked windows
					WinAPI.UnhookWindowsHookEx(hookHandle);					
					base.WndProc(ref m);
					break;
				default:
					base.WndProc(ref m);
					break;
			}
		}
		
		#endregion

		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if( components != null )
					components.Dispose();
			}
			base.Dispose( disposing );
		}

		#endregion

		#region Public properties

		public Color BorderColor
		{
			get{return borderColor;}
			set{borderColor = value;}
		}

		public MenuStyle MenuStyle
		{
			get{return menuStyle;}
			set
			{
				if(value == MenuStyle.Default)
					WinAPI.UnhookWindowsHookEx(hookHandle);
				else
					hookHandle = SetWindowsHookEx(4, hookProc, IntPtr.Zero ,
						WinAPI.GetWindowThreadProcessId(Handle,0));
				menuStyle = value;
			}
		}

		#endregion

		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}
		#endregion
	}
}
